Skip to content

Reject FTP commands with parameters containing CR characters#2453

Open
somecookie wants to merge 1 commit into
squid-cache:masterfrom
measurement-factory:OSD-7-reject-CR-in-parsing
Open

Reject FTP commands with parameters containing CR characters#2453
somecookie wants to merge 1 commit into
squid-cache:masterfrom
measurement-factory:OSD-7-reject-CR-in-parsing

Conversation

@somecookie

@somecookie somecookie commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

FTP command syntax treats CR and LF characters as command terminators.
All RFC 959 sightings of CR and LF either refer to the CRLF terminator
or treat both characters the same way. For example, RFC 959 defines an
FTP command parameter as a sequence of chars, where:

<char> ::= any of the 128 ASCII characters except <CR> and <LF>

Squid should (and now does) restrict CR use to the command terminator
sequence, especially since some FTP servers treat CRs as command
delimiters -- if we continue to allow embedded CRs in FTP command
parameters than our FtpClient::writeCommand() will assert when trying to
forward those commands to the FTP server. Moreover, we already use that
CR treatment when parsing FTP responses (see
Ftp::Client::parseControlReply()).

When it comes to command termination, CRs are still optional.

A new ban on CRs in FTP command parameter values means that Squid starts
treating some FTP commands as syntactically invalid, using
EarlyErrorKind::MalformedCommand for the first time since its inception
in 2014 commit eacfca8. For example, PWD\rQUIT input is now invalid.

This is a Measurement Factory project.

FTP command syntax treats CR and LF characters as command terminators.
All RFC 959 sightings of CR and LF either refer to the CRLF terminator
or treat both characters the same way. For example, RFC 959 defines an
FTP command parameter as a sequence of chars, where:

    <char> ::= any of the 128 ASCII characters except <CR> and <LF>

Squid should (and now does) restrict CR use to the command terminator
sequence, especially since some FTP servers treat CRs as command
delimiters -- if we continue to allow embedded CRs in FTP command
parameters than our FtpClient::writeCommand() will assert when trying to
forward those commands to the FTP server. Moreover, we already use that
CR treatment when _parsing_ FTP responses (see
Ftp::Client::parseControlReply()).

When it comes to command termination, CRs are still optional.

A new ban on CRs in FTP command parameter values means that Squid starts
treating some FTP commands as syntactically invalid, using
EarlyErrorKind::MalformedCommand for the first time since its inception
in 2014 commit eacfca8. For example, `PWD\rQUIT` input is now invalid.

N.B. This change removes "RFC 959 Section 3.1.1.5.2" reference. That
reference was wrong: RFC Section 3.1 is about "data representations"
used for file transfers rather than for command syntax. I probably
missed this problem when the addition of that reference was requested at
http://www.squid-cache.org/mail-archive/squid-dev/201408/0023.html
Comment thread src/servers/FtpServer.cc
// inner_space_char = SP / HT / VT / NP ; space_char without CR and LF
// space_char = SP / HT / VT / NP / CR / LF; any isspace(3) character in "C" locale

static const auto InlineSpaceChars = " \f\t\v";

@somecookie somecookie Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we align the names with the definition to make the code easier to read, e.g.,

Current Grammar token Suggested name
InlineSpace inner_space_char InnerSpaceChars
FullWhiteSpace space_char SpaceChars
CommandChars code_char CodeChars
TailChars parameter_char ParameterChars
LeadingSpace BWS BadWhiteSpace

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided not to go for this to keep the diff minimal which makes it clearer what is changed in the parsing logic

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also noticed the mismatch. It should be fixed. The only question is when: In this PR or in a dedicated polishing followup.

When sketching this code, I preserved old names and old naming patterns because I did not want to change otherwise unchanged official code lines. Emphasizing what we are changing felt more important to me. It still does. However, if others explicitly request that we polish names in this PR (at the expense of diff clarity), I will not object.

Comment thread src/servers/FtpServer.cc
Comment thread src/servers/FtpServer.cc
@@ -663,7 +679,7 @@ Ftp::Server::parseOneRequest()
(void)tok.skipAll(LeadingSpace); // leading OWS and empty commands

@somecookie somecookie Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(void)tok.skipAll(LeadingSpace); // leading OWS and empty commands
(void)tok.skipAll(LeadingSpace); // leading BWS

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as for variable names above

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would keep this otherwise unchanged line intact because the old comment is not wrong -- old "OWS and empty commands" description matches LeadingSpace definition in PR code. If others want to polish this comment, here is a better variation (BWS includes "empty commands"):

Suggested change
(void)tok.skipAll(LeadingSpace); // leading OWS and empty commands
(void)tok.skipAll(LeadingSpace); // leading BWS

or, if you prefer, just

Suggested change
(void)tok.skipAll(LeadingSpace); // leading OWS and empty commands
(void)tok.skipAll(LeadingSpace); // BWS

Again, I would not commit this comment change unless explicitly requested by others.

@somecookie somecookie closed this Jun 29, 2026
@somecookie somecookie deleted the OSD-7-reject-CR-in-parsing branch June 29, 2026 12:09
@squid-anubis squid-anubis added the M-failed-other https://github.com/measurement-factory/anubis#pull-request-labels label Jun 29, 2026
@somecookie somecookie restored the OSD-7-reject-CR-in-parsing branch June 29, 2026 12:13
@somecookie somecookie changed the title [WIP] Reject FTP commands with parameters containing CR characters Reject FTP commands with parameters containing CR characters Jun 29, 2026
@somecookie somecookie reopened this Jun 29, 2026
@somecookie somecookie marked this pull request as ready for review June 29, 2026 13:24
@rousskov rousskov removed the M-failed-other https://github.com/measurement-factory/anubis#pull-request-labels label Jun 29, 2026

@rousskov rousskov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for testing and polishing this improvement.

@rousskov rousskov added S-waiting-for-more-reviewers needs a reviewer and/or a second opinion S-could-use-an-approval An approval may speed this PR merger (but is not required) labels Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-could-use-an-approval An approval may speed this PR merger (but is not required) S-waiting-for-more-reviewers needs a reviewer and/or a second opinion

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants